package uk.co.q3c.deplan.server.dao;

import java.util.List;
import java.util.Map;

import uk.co.q3c.deplan.client.domain.DomainObject;
import uk.co.q3c.deplan.client.domain.dag.ChangeInvalidException;
import uk.co.q3c.deplan.client.domain.dag.CycleDetectedException;
import uk.co.q3c.deplan.client.domain.dag.SharedStructure;
import uk.co.q3c.deplan.client.domain.dag.SharedStructureChangeLog;
import uk.co.q3c.deplan.client.domain.resource.PersistenceReferenceException;
import uk.co.q3c.deplan.client.service.TaskService;

import com.googlecode.objectify.Key;
import com.googlecode.objectify.Query;

/**
 * A Dao implementation has two implementation one for client-side and one for server-side implementation. The interface for both is the
 * same so that domain objects can call on the Dao for data access. <br>
 * <br>
 * <li>it provides access to to the datastore in a reasonably programmer friendly way, providing a base for higher level Dao's (for example
 * taskDao) as needed, and a common interface for common data access tasks. On the client side, these would be implemented by invoking RPC
 * service calls, and on the server side by implementing calls to the datastore. Interestingly, a call from the client side would usually
 * also call the server side implementation as a result of invoking the service <li>
 * 
 * <br>
 * If desired, a "session" can be closed explicitly either by disposing of the Dao, or by invoking {@link #closeSession()} *
 * 
 * @see DomainDao_BigT
 * @see TaskService
 * @author DSowerby 23 Aug 2008 10:54:56
 * 
 * @param <E>
 */
public interface DomainDao {

	/**
	 * Forces a new datastore instance (@link Objectify) to be created.
	 */
	void closeSession();

	/**
	 * @see DomainDao_BigT#countAll(Class)
	 * @param clazz
	 * @return
	 */
	int countAll(Class<? extends DomainObject> clazz);

	int countDiscarded(Class<? extends DomainObject> clazz);

	int countUndiscarded(Class<? extends DomainObject> clazz);

	void deleteAll(Class<? extends DomainObject> clazz);

	void deleteDiscarded(Class<? extends DomainObject> clazz);

	void deleteUndiscarded(Class<? extends DomainObject> clazz);

	/**
	 * Sets the domain object's discarded flag to true and saves it. If you don't want to save it automatically, set the discarded flag
	 * directly on the domain object
	 * 
	 * @param dob
	 * @throws CycleDetectedException
	 * @throws ChangeInvalidException
	 * @throws PersistenceReferenceException
	 */
	void discard(DomainObject dob) throws CycleDetectedException, ChangeInvalidException, PersistenceReferenceException;

	<T extends DomainObject> List<T> findAllList(Class<T> clazz);

	List<? extends DomainObject> findAllList(List<Class<? extends DomainObject>> classes, String condition, Object value);

	<T extends DomainObject> List<T> findDiscardedList(Class<T> clazz);

	/**
	 * @see DomainDao_BigT#findDiscardedList(List, String, Object)
	 * @param classes
	 * @param condition
	 * @param value
	 * @return
	 */
	List<? extends DomainObject> findDiscardedList(List<Class<? extends DomainObject>> classes, String condition, Object value);

	DomainObject findFirstFromAll(List<Class<? extends DomainObject>> classes, String condition, Object value);

	DomainObject findFirstFromDiscarded(List<Class<? extends DomainObject>> classes, String condition, Object value);

	DomainObject findFirstFromUndiscarded(List<Class<? extends DomainObject>> classes, String condition, Object value);

	<T extends DomainObject> List<T> findUndiscardedList(Class<T> clazz);

	/**
	 * @see DomainDao_BigT#findUndiscardedList(com.googlecode.objectify.Query)
	 * @param classes
	 * @param condition
	 * @param value
	 * @return
	 */
	List<? extends DomainObject> findUndiscardedList(List<Class<? extends DomainObject>> classes, String condition, Object value);

	<T extends DomainObject> List<T> getList(List<Key<T>> keys);

	/**
	 * @See dao_b
	 * @param <T>
	 * @param keys
	 * @return
	 */
	<T extends DomainObject> List<T> getListOf(List<Key<? extends T>> keys);

	Map<Key<DomainObject>, DomainObject> getMap(List<Key<? extends DomainObject>> keys);

	/**
	 * @see DomainDao_BigT#getMap(List)
	 * @param <T>
	 * @param keys
	 * @return
	 */
	<T extends DomainObject> Map<Key<T>, T> getMapOf(List<Key<T>> keys);

	/**
	 * @see DomainDao_BigT#getObjectFromKey(Key)
	 * 
	 * @param <T>
	 * @param key
	 * @return
	 */

	<T extends Object> T getObjectFromKey(Key<T> key);

	/**
	 * <b>NOTE</b> This assumes that domainObject has no parent entity. Returns null if domainObject is null
	 * 
	 * @see DomainDao_BigT#keyFor(DomainObject)
	 * @param domainObject
	 *            the object you want the key for
	 * @return
	 */
	<T extends DomainObject> Key<T> keyFor(T domainObject);

	<T extends DomainObject> Query<T> newQuery(Class<T> clazz);

	void save(DomainObject dob);

	/**
	 * @see DomainDao_BigT#saveShared(SharedStructure, Key, SharedStructureChangeLog)
	 * @param inMemoryInstance
	 * @param storedInstanceKey
	 * @param changeLog
	 * @return
	 */
	SharedStructure saveShared(SharedStructure inMemoryInstance, Key<? extends SharedStructure> storedInstanceKey, SharedStructureChangeLog changeLog);

	int executeCount(Query<? extends DomainObject> query);

}
